' This module contains code borrowed from Difference's post here: http://www.monkeycoder.co.nz/Community/posts.php?topic=1659#18828
' Function used are:
'
' PolygonArea by Difference
' PolyCentroid by impixi
'

Import yimDebug
Import yimCollision

Class yimPoly
	
	Field polyPts:Float[]
	Field tFormPolyPts:Float[]
	
	Field ang:Float = 0.0
	Field pos:yimVec2D = New yimVec2D(0, 0)
	
	Field center:yimVec2D = New yimVec2D(0,0)
	Field scale:yimVec2D = New yimVec2D(1, 1)
	
	Method GetX:Float(i:Int=0)
		Return tFormPolyPts[i*2]
	End
	
	Method GetY:Float(i:Int=0)
		Return tFormPolyPts[i*2 + 1]
	End
	
	Method Update()
		tFormPolyPts = Collision.TFormPoly(polyPts, pos.x, pos.y, ang, scale.x, scale.y, center.x, center.y)
	End
	
	Method IsPointIn:Bool(px, py)

		Return Collision.PointInPoly(px, py, tFormPolyPts)
		
	End
	
	Method SetAngle(angle:Float)
		ang = angle
	End
	
	Method Draw(filled:Bool=False)
		If filled
			DrawPoly(tFormPolyPts)
		Else
			DrawUnfilledPoly(tFormPolyPts)
		EndIf
	End
	
	Method SetCentroid(centroid:Float[])
		center.Set(centroid[0], centroid[1])
	End
	
	Method SetPosition(x:Float, y:Float)
		pos.Set(x, y)
	End
	
	Method SetScale(sX:Float, sY:Float)
		scale.Set(sX, sY)
	End
		
End

Class yimRect Extends yimPoly

	Method New(_x:Float, _y:Float, _w:Float, _h:Float)
		Set(_x, _y, _w, _h)
	End
	
	Method Set(x:Float, y:Float, w:Float, h:Float, angle:Float=0)
		ang = angle
		polyPts = [0, 0, w, 0, w, h, 0, h]
		SetPosition(x, y)
		SetCentroid(PolyCentroid(polyPts))
		Update()
	End
	
	Method GetWidth:Float()
		Return polyPts[2]
	End
	
	Method GetHeight:Float()
		Return polyPts[5]
	End
End

Class yimVec2D
	Field x:Float
	Field y:Float
	
	Method New(pX:Float=0.0, pY:Float=0.0)
		x = pX
		y = pY
	End
	
	Method Copy(copyVec:yimVec2D)
		x = copyVec.x
		y = copyVec.y
	End
	
	Method Set(_x:Float, _y:Float)
	 	x = _x
	 	y = _y
	End
End

Function ToRadians:Float(deg:Float)
	Return (deg*PI)/180.0
End

Function ToDegrees:Float(radians:Float)
	Return (radians*180.0)/PI
End

Function Dist2D:Float(x1:Float, y1:Float, x2:Float, y2:Float)
	Return Sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
End

Function TweenValue(startVal:Float, endVal:Float, speed:Float)
	Local diff:Float = endVal-startVal
	Local absDiff:Float = Abs(diff)
	
	If absDiff <= speed
		Return endVal
	Else
		Local speedDir:Int = diff/absDiff
		
		Return (startVal + speed*speedDir)
	EndIf
End

Function ValDir:Int(fromVal:Float, toVal:Float)
	Local diff:Float = toVal - fromVal
	Return Int(Abs(diff)/diff)
End

Function WrapAngle(ang:Float)
	If ang < 0.0
		Return ang + 360.0
	ElseIf ang >= 360.0
		Return ang - 360.0
	Else
		Return ang
	EndIf
End

Function RectInRect:Bool(rect1:yimRect, rect2:yimRect)
	Return Collision.PolyToPoly(rect1.tFormPolyPts, rect2.tFormPolyPts)
End

Function RectInRect:Bool(x1:Float, y1:Float, w1:Float, h1:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	Return (x1 < x2+w2 And x1+w1 > x2 And y1 < y2+h2 And y1+h1 > y2)
End

Function CircleInRect:Bool(cX:Float, cY:Float, cR:Float, rect:yimRect)	
	#Rem 
	Local hRW:Float = rect.w/2.0
	Local hRH:Float = rect.h/2.0
	
    Local circleDistanceX:= Abs(cX - rect.x - hRW)
    Local circleDistanceY:= Abs(cY - rect.y - hRH)
	
    If circleDistanceX > (hRW + cR)
		Return False
	EndIf
	
    If circleDistanceY > (hRH + cR)
    	Return False
	EndIf

    If circleDistanceX <= hRW
		Return True
    EndIf
    
    If circleDistanceY <= hRH
		Return True
	EndIf

    Local cornerDist:Float = Pow(circleDistanceX - hRW, 2) + Pow(circleDistanceY - hRH, 2)
	#End
	
    Return False '(cornerDist <= cR*cR)
End

Function GetRectOverlap:yimVec2D(rA:yimRect, rB:yimRect, outVec:yimVec2D=Null)
	#Rem
	Local rAh:=rA.h/2.0
	Local rAw:=rA.w/2.0
	
	Local rBh:=rB.h/2.0
	Local rBw:=rB.w/2.0
	
	Local rACenter:yimVec2D = New yimVec2D(rA.x+rAw, rA.y+rAh)
	Local rBCenter:yimVec2D = New yimVec2D(rB.x+rBw, rB.y+rBh)

    Local distanceX:Float = rACenter.x - rBCenter.x
	Local distanceY:Float = rACenter.y - rBCenter.y
	Local minDistanceX:Float = rAw + rBw
	Local minDistanceY:Float = rAh + rBh
	
   	If Abs(distanceX) >= minDistanceX Or Abs(distanceY) >= minDistanceY
        If outVec
        	outVec.Set(0,0)
        	Return Null
        Else
        	Return New yimVec2D(0,0)
		EndIf
	End

	Local depthX:Float = 0-minDistanceX - distanceX
	Local depthY:Float = 0-minDistanceY - distanceY
	
	If distanceX > 0
		depthX = minDistanceX - distanceX
	EndIf
	
	If distanceY > 0
		depthY = minDistanceY - distanceY
	EndIf
	
	If outVec
		outVec.Set(depthX, depthY)
		Return Null
	Else
		Return New yimVec2D(depthX, depthY)
	EndIf
	#End
	Return New yimVec2D(0,0)
End

Function PolygonArea:Float(polyPoints:Float[])

	Local accum:Float
	Local  j:Int = polyPoints.Length()-2

	For Local i:Int =0 Until polyPoints.Length() Step 2
		accum += polyPoints[j] * polyPoints[i+1] - polyPoints[i] * polyPoints[j+1]
    		j=i 
	Next

	Return accum / 2

End Function

Function PolyCentroid:Float[](poly:Float[])

	Local result:Float[2]	'i0 = x, i1 = y
    result[0] = 0
    result[1] = 0

    For Local i:Int = 0 Until (poly.Length - 2) Step 2
		result[0] += (poly[i] + poly[i+2]) * ((poly[i] * poly[i+3]) - (poly[i+2] * poly[i+1]))
		result[1] += (poly[i+1] + poly[i+3]) * ((poly[i] * poly[i+3]) - (poly[i+2] * poly[i+1]))
    Next

    Local area:Float = PolygonArea(poly)

    result[0] = Abs(result[0] / 6.0 / area)
    result[1] = Abs(result[1] / 6.0 / area)

	Return result

End

Function CatmullRomPoint(pOut:yimVec2D, p0:yimVec2D, p1:yimVec2D, p2:yimVec2D, p3:yimVec2D, t:Float)
	Local t2:Float = t*t
	Local t3:Float = t2 * t
	
	pOut.x = 0.5 * ( ( 2.0 * p1.x ) + ( -p0.x + p2.x ) * t + ( 2.0 * p0.x - 5.0 * p1.x + 4 * p2.x - p3.x ) * t2 + ( -p0.x + 3.0 * p1.x - 3.0 * p2.x + p3.x ) * t3 )
	pOut.y = 0.5 * ( ( 2.0 * p1.y ) + ( -p0.y + p2.y ) * t + ( 2.0 * p0.y - 5.0 * p1.y + 4 * p2.y - p3.y ) * t2 + ( -p0.y + 3.0 * p1.y - 3.0 * p2.y + p3.y ) * t3 )

End Function
